home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / general / viewers / polyview / polyvw31.lha / Polyview3.1 / new / pvparse.c < prev    next >
C/C++ Source or Header  |  1993-06-23  |  16KB  |  519 lines

  1. /*****************************************************************************
  2.  * NCSA Polyview 3.0                                                         *
  3.  *                                                                           *
  4.  * Version 3 changes and additions by Marc Andreessen.                       *
  5.  * Version 2 by Brian Calvert.                                               *
  6.  *                                                                           *
  7.  * Software Development Group                                                *
  8.  * National Center for Supercomputing Applications                           *
  9.  * University of Illinois at Urbana-Champaign                                *
  10.  *                                                                           *
  11.  * This is BETA release software.  As such it may contain software bugs and  *
  12.  * exhibit inconsistencies.                                                  *
  13.  *                                                                           *
  14.  * Please send bug reports to polyview@ncsa.uiuc.edu.                        *
  15.  *                                                                           *
  16.  * Copyright (c) 1992 The Board of Trustees of the University of Illinois.   *
  17.  *                                                                           *
  18.  * Permission to use, copy, and modify this software and its                 *
  19.  * documentation for educational, research, and non-profit purposes is       *
  20.  * hereby granted, provided that the above copyright notice, the original    *
  21.  * authors names, and this permission notice appear in all such copies.      *
  22.  * Any distribution of this software requires the explicit and written       *
  23.  * authorization of the authors.                                             *
  24.  *                                                                           *
  25.  * The University of Illinois makes no representations about the             *
  26.  * suitability of this software for any purpose.  It is provided "as is"     *
  27.  * without warranty of any kind.                                             *
  28.  *****************************************************************************/
  29.  
  30. /* $Header: /usr3/people/gbourhis/pv3/new/RCS/pvparse.c,v 1.1 92/09/18 10:55:26 marca Exp $ */
  31.  
  32. #ifdef RCSLOG
  33. $Log:    pvparse.c,v $
  34.  * Revision 1.1  92/09/18  10:55:26  marca
  35.  * Initial revision
  36.  * 
  37. #endif
  38.  
  39. #include "pv.h"
  40.  
  41. /*
  42.   ACTION FORMAT:  The syntax of user-entered commands is defined by the action
  43.   format.  An action format is a string of mandatory and optional keywords and
  44.   values (collectively referred to as terms).
  45.   
  46.   Mandatory terms are simply stated in the format string.  Optional terms are
  47.   enclosed in square brackets.  Optional terms may be nested.
  48.   
  49.   Terms may be either keywords or values.  Keywords are simply stated as
  50.   alphanumeric strings.  The syntax of values are enclosed in angle brackets.
  51.   */
  52.  
  53.  
  54. /* Additions:
  55.    help option
  56.    identify where the syntax error occurs
  57.    identify  all the commands that an ambiguous entry matches
  58.    prompt for additional information (for interactive only?)
  59.    */
  60.  
  61.  
  62.  
  63. #define COMMENT_CHAR    ';'
  64.  
  65.  
  66. char *get_keyword_arg(int argc, arg_t arg[], int keynum)
  67. {
  68.   /* If the keyword number is larger than the number of arguments, */
  69.   /* then it doesn't exist. */
  70.   if (keynum >= argc)
  71.     return NULL;
  72.   
  73.   /* If the argument is a keyword, return its value.  Otherwise, */
  74.   /* return NULL to indicate no match. */
  75.   if (arg[keynum].type == PVKEYWORD)
  76.     return arg[keynum].value;
  77.   else
  78.     return NULL;
  79. }
  80.  
  81.  
  82. char * get_string_arg(int argc, arg_t arg[], char *name, char *def)
  83. {
  84.   for (argc--; argc > 0; argc--) 
  85.     if ((arg[argc].type == PVSTRING) &&
  86.         (strcasecmp(arg[argc].description, name) == 0))
  87.       return arg[argc].value;
  88.   
  89.   /* If no match was made, return the default. */
  90.   return def;
  91. }
  92.  
  93.  
  94. float get_float_arg(int argc, arg_t arg[], char *name, float def)
  95. {
  96.   for (argc--; argc > 0; argc--)
  97.     if ((arg[argc].type == PVFLOAT) &&
  98.         (strcasecmp(arg[argc].description, name) == 0))
  99.       return ((float)atof (arg[argc].value));
  100.   
  101.   /* If no match was made, return the default. */
  102.   return def;
  103. }
  104.  
  105.  
  106. static int match_term(char [], char [], int *, arg_t []);
  107. static char * get_token(char **, char *, char, char);
  108. static char * get_linetoken(char **, char *);
  109. static char * get_formattoken(char **, char *);
  110.  
  111.  
  112. int load_script (state_t *state, FILE *fp)
  113. {
  114.   char            line[MAXLINELEN];
  115.   
  116.   while (fgets(line, MAXLINELEN, fp) != NULL) 
  117.     {
  118.       line[strlen(line)-1] = '\0';
  119.       parse_line(state, line, FALSE);
  120.     }
  121.   
  122.   return ST_OKAY;
  123. }
  124.  
  125.  
  126. int parse_line (state_t *state, char *line, int immediate)
  127. {
  128.   char formattoken[MAXFORMATLEN];
  129.   char linetoken[MAXFORMATLEN];
  130.   
  131.   char *lineptr;
  132.   char *tokenptr;
  133.   parse_t *parseptr;
  134.   parse_t *matchptr;
  135.   
  136.   int matches;
  137.   int match;
  138.   
  139.   int i;
  140.   int argc;
  141.   int margc;
  142.   arg_t arg[MAXACTIONARGS];
  143.   arg_t marg[MAXACTIONARGS];
  144.   action_t *action;
  145.   
  146.   stprintf (state, " ");
  147.   
  148.   /* Echo the line to the transcript file. */
  149.   /* BUT DON'T WRITE QUIT! */
  150.   /* Also don't write 'do filename'. */
  151.   if (state->transcript_fp != NULL &&
  152.       strncmp (line, "quit", 4) != 0 &&
  153.       strncmp (line, "do ", 3) != 0 &&
  154.       strncmp (line, "script", 6) != 0)
  155.     fprintf(state->transcript_fp, "%s\n", line);
  156.  
  157.   /* Strip off anything that follows a comment character. */
  158.   if ( (lineptr = strchr(line, (int) COMMENT_CHAR)) != NULL)
  159.     *lineptr = '\0';
  160.   
  161.   /* Skip any leading blanks.  If there is nothing to the remaining */
  162.   /* string, exit. */
  163.   for (lineptr = line; *lineptr == ' '; lineptr++);
  164.   if (*lineptr == '\0')
  165.     return ST_OKAY;
  166.  
  167.   /* Add to command box's history list; since we wait until now,
  168.      we make sure comments/blank lines aren't put into list. */
  169.   if (state->last_cmd_already_added)
  170.     state->last_cmd_already_added = FALSE;
  171.   else
  172.     {
  173.       if (strncmp (line, "do", 2) != 0 &&
  174.           strncmp (line, "script", 6) != 0)
  175.         GUIaddCommand (line);
  176.     }
  177.  
  178.   matches = 0;
  179.   for (parseptr = state->parse_table; parseptr->format[0] != '\0';
  180.        parseptr++)
  181.     {
  182.       match = TRUE;
  183.       lineptr = line;
  184.       tokenptr = parseptr->format;
  185.       argc = 0;
  186.       
  187.       while ( ((*tokenptr) || (*lineptr)) && match)
  188.         {
  189.           get_linetoken(&lineptr, linetoken);
  190.           get_formattoken(&tokenptr, formattoken);
  191.  
  192.           fflush (stdout);
  193.           if (match_term(linetoken, formattoken, &argc, arg) != ST_OKAY)
  194.             match = FALSE;
  195.           fflush (stdout);
  196.         }
  197.       
  198.       if (match)
  199.         {
  200.           matchptr = parseptr;
  201.           matches++;
  202.           fflush (stdout);
  203.           margc = argc;
  204.           for (i = 0; i < argc; i++)
  205.             marg[i] = arg[i];
  206.         }
  207.     }
  208.   
  209.   if (matches < 1)
  210.     {
  211.       stprintf(state, "SYNTAX ERROR:  %s\n", line);
  212.       return ST_ERROR;
  213.     }
  214.   else if (matches > 1)
  215.     {
  216.       stprintf(state, "AMBIGUOUS:  %s\n", line);
  217.       return ST_ERROR;
  218.     }
  219.   
  220.   /* Either immediately execute the command or add it to the action */
  221.   /* list. */
  222.   if (immediate || matchptr->immediate)
  223.     {
  224.       if (matchptr->action_fn == NULL)
  225.         printf("Immediate execution, but NULL pointer!\n");
  226.       else
  227.         {
  228.           /* Create a new action record and pass it to the */
  229.           /* function. */
  230.           action = new_action(state, matchptr->action_fn,
  231.                               margc, marg, /*steps*/ 1);
  232.           
  233.           action->global = matchptr->global;
  234.           do_action(state, action);
  235.           
  236.           free_action(state, action);
  237.         }
  238.     }
  239.   else
  240.     add_action(state, matchptr->action_fn, margc, marg,
  241.                /*steps*/ 1, matchptr->global, /*at_current*/ FALSE);
  242.  
  243.   return ST_OKAY;
  244. }
  245.  
  246.  
  247. int AddArg (int *argc, arg_t *arg, char *term_format, char *term)
  248. {
  249.   /* Dummy string for test for float. */
  250.   char float_test[5];
  251.  
  252.   /* Copy the name of the value into the arg descriptor table. */
  253.   arg[*argc].description = (char *) PVMALLOC(strlen(term_format)-4);
  254.   sscanf(&term_format[4], "%[^>]>", arg[*argc].description);
  255.   
  256.   /* If the term_format is a value, then get the type of the value */
  257.   /* and attempt to read the value into a malloc'd data area.  Set */
  258.   /* the arg type and value. */
  259.   switch (term_format[2]) {
  260.   case 's':
  261.     arg[*argc].type = PVSTRING;
  262.     arg[*argc].value = (char *) PVMALLOC(strlen(term)+1);
  263.     
  264.     strcpy(arg[*argc].value, term);
  265.     break;
  266.   case 'f':
  267.     arg[*argc].type = PVFLOAT;
  268.     arg[*argc].value = (char *) PVMALLOC(strlen (term)+1);
  269.  
  270.     strcpy (arg[*argc].value, term);
  271.     arg[*argc].value[strlen (term)] = '\0';
  272.  
  273.     /* Check to see if we're dealing with a float. */
  274.     if (sscanf(term, "%f", (float *)float_test) != 1)
  275.       {
  276.         arg[*argc].type = PVDEFAULT;
  277.         PVFREE(arg[*argc].description);
  278.         PVFREE(arg[*argc].value);
  279.         return ST_ERROR;
  280.       }
  281.  
  282.     break;
  283.   default:
  284.     printf("SYSTEM ERROR:  '%s' is not ", term_format);
  285.     printf("supported by match_term.\n");
  286.     return ST_ERROR;
  287.   }
  288.   
  289.   return ST_OKAY;
  290. }
  291.  
  292.  
  293. static int match_term  (char term[], char term_format[], 
  294.                         int *argc, arg_t arg[])
  295. {
  296.   char            *term_formatptr;
  297.   char            subterm[MAXFORMATLEN];
  298.   
  299.   int            match_count;
  300.   
  301.   fflush (stdout);
  302.   if (strncmp(term_format, "<%", 2) == 0)
  303.     {
  304.       if (*term == '\0') 
  305.         {
  306.           /* Return ST_EMPTYSTRING if term is empty since there's */
  307.           /* nothing to parse. */
  308.           return ST_EMPTYSTRING;
  309.         }
  310.       
  311.       /* Add the argument to the list. */
  312.       if (AddArg(argc, arg, term_format, term) == ST_ERROR) 
  313.         {
  314.           return ST_ERROR;
  315.         }
  316.     }
  317.   else if (term_format[0] == '[')
  318.     {
  319.       /* If the term format is an optional parameter, strip the brackets */
  320.       /* from the end of the string and pass a pointer to the character */
  321.       /* after the opening bracket back into the routine so that it can */
  322.       /* be recursively parsed. */
  323.       term_format[strlen(term_format)-1] = '\0';
  324.       if (match_term(term, &term_format[1], argc, arg) == ST_ERROR)
  325.         return ST_ERROR;
  326.       else
  327.         return ST_OKAY;
  328.     }
  329.   else if (strpbrk(term_format, "|") != NULL)
  330.     {
  331.       term_formatptr = term_format;
  332.       
  333.       if (*term == '\0')
  334.         {
  335.           /* If the term does not exist, then default to the */
  336.           /* first subterm of the ORed list. */
  337.           get_token(&term_formatptr, subterm, '|', '\0');
  338.           
  339.           /* If the term is a keyword, return it as an */
  340.           /* argument just in case this is an optional */
  341.           /* list (in optional ORed lists of keyowrds, the */
  342.           /* first keyword is the default value. */
  343.           if ( (subterm[0] != '<') && (subterm[0] != '[') )
  344.             {
  345.               arg[*argc].description = (char *) PVMALLOC(sizeof(char));
  346.               *(arg[*argc].description) = '\0';
  347.               arg[*argc].type = PVKEYWORD;
  348.               arg[*argc].value = (char *) PVMALLOC(strlen(subterm)+1);
  349.               strcpy(arg[*argc].value, subterm);
  350.               (*argc)++;
  351.             }
  352.           
  353.           return ST_EMPTYSTRING;
  354.         }
  355.       else
  356.         {
  357.           /* If the term format is an ORed set of options, */
  358.           /* then beak it down into subterms and recursively */
  359.           /* attempt to match them to the term. */
  360.           match_count = 0;
  361.           while (*term_formatptr != '\0')
  362.             {
  363.               /* Get the next ORed subterm from the term */
  364.               /* and call this function again to parse it. */
  365.               get_token(&term_formatptr, subterm, '|', '\0');
  366.               
  367.               if (match_term(term, subterm, argc, arg) == ST_OKAY)
  368.                 match_count++;
  369.             }
  370.           
  371.           if (match_count == 1)
  372.             /* If only one subterm matched, then the ORed list */
  373.             /* was successfully matched.  */
  374.             return ST_OKAY;
  375.         }
  376.       return ST_ERROR;
  377.     }
  378.   else if (*term == '\0')
  379.     {
  380.       if (*term_format == '\0')
  381.         return ST_OKAY;
  382.       else
  383.         /* Return ST_EMPTYSTRING if the term is empty but the format */
  384.         /* is not. */
  385.         return ST_EMPTYSTRING;
  386.     }
  387.   else
  388.     {
  389.       /* If term_format is not a value, set the arg type to PVKEYWORD */
  390.       /* and arg value to the keyword string. */
  391.       /* There is a mismatch error if the term is empty but the */
  392.       /* format is required, or if the given characters of the */
  393.       /* term don't match the corresponding characters of the */
  394.       /* term_format. */
  395.       if ( ((strlen(term) == 0) && (strlen(term_format) != 0)) ||
  396.           (strncasecmp(term, term_format, strlen(term)) != 0) )
  397.         return ST_ERROR;
  398.  
  399.       arg[*argc].description = (char *) PVMALLOC(sizeof(char));
  400.       *(arg[*argc].description) = '\0';
  401.       arg[*argc].type = PVKEYWORD;
  402.       arg[*argc].value = (char *) PVMALLOC(strlen(term_format)+1);
  403.       strcpy(arg[*argc].value, term_format);
  404.     }
  405.   
  406.   /* Increment the argument count. */
  407.   (*argc)++;
  408.   return ST_OKAY;
  409. }
  410.  
  411.  
  412. static char *skip_blanks(char *str)
  413. {
  414.   if (str == NULL)
  415.     return NULL;
  416.   
  417.   while ( ((*str == ' ') || (*str == '\t')) && (*str != '\0') )
  418.     str++;
  419.   
  420.   return str;
  421. }
  422.  
  423.  
  424. static char *get_token(char **token, char *copy, char endtoken, char captoken)
  425. {
  426.   char        *returnptr;
  427.     
  428.   /* We will return the pointer to the beginning of the copy buffer. */
  429.   returnptr = copy;
  430.   
  431.   /* Copy the token until we reach the terminating character, the */
  432.   /* end of the string, or a comment character. */
  433.   for (; (**token != endtoken) && (**token != '\0') &&
  434.        (**token != COMMENT_CHAR); copy++, (*token)++)
  435.     *copy = **token;
  436.     
  437.   /* If we haven't hit the end of the token string yet, increment the */
  438.   /* token string pointer. */
  439.   if ((**token != '\0') && (**token != COMMENT_CHAR))
  440.     (*token)++;
  441.     
  442.   /* Terminate the string with the closing character provided by the */
  443.   /* caller, and a zero character (which may be redundant). */
  444.   *(copy++) = captoken;
  445.   *(copy) = '\0';
  446.   
  447.   return returnptr;
  448. }
  449.  
  450.  
  451. static char *get_linetoken(char **linestring, char *copystring)
  452. {
  453.   char        *copychar;
  454.  
  455.   /* Initialize the return string by making its first character a */
  456.   /* string-terminator. */
  457.   *copystring = '\0';
  458.   copychar = copystring;
  459.   
  460.   /* Skip any leading blanks in the token string. */
  461.   *linestring = skip_blanks(*linestring);
  462.   
  463.   /* Check the first character of the string to determine whether */
  464.   /* it is a comment (';'), a quoted string ('\"'), or a regular */
  465.   /* keyword (anything else). */
  466.   switch (**linestring)
  467.     {
  468.     case COMMENT_CHAR:    /* Commment */
  469.       /* If we've hit a comment, return a pointer to an empty */
  470.       /* string.  This signals the calling routine that there's */
  471.       /* nothing more to read. */
  472.       *linestring = copystring;
  473.       break;
  474.     case '\"':        /* Quoted string token */
  475.       (*linestring)++;
  476.       get_token(linestring, copychar, '\"', '\0');
  477.       break;
  478.     default:        /* Keyword token */
  479.       get_token(linestring, copychar, ' ', '\0');
  480.       break;
  481.     }
  482.   
  483.   return copychar;
  484. }
  485.  
  486.  
  487. static char *get_formattoken(char **formatstring, char *copystring)
  488. {
  489.   char        *copychar;
  490.   
  491.   /* Initialize the return string by making its first character a */
  492.   /* string-terminator. */
  493.   *copystring = '\0';
  494.   copychar = copystring;
  495.   
  496.   /* Skip any leading blanks in the token string. */
  497.   *formatstring = skip_blanks(*formatstring);
  498.   
  499.   /* Check the first character of the string to determine whether */
  500.   /* it is an optional parameter ('['), a value ('<'), the end of */
  501.   /* the string ('\0'), or a keyword (anything else). */
  502.   switch (**formatstring)
  503.     {
  504.     case '[':        /* Optional token */
  505.       get_token(formatstring, copychar, ']', ']');
  506.       break;
  507.     case '<':        /* Value token */
  508.       get_token(formatstring, copychar, '>', '>');
  509.       break;
  510.     case '\0':        /* No token */
  511.       break;
  512.     default:        /* Keyword token */
  513.       get_token(formatstring, copychar, ' ', '\0');
  514.       break;
  515.     }
  516.   
  517.   return copystring;
  518. }
  519.